package com.jiangyg.mall.authz.config;

import com.jiangyg.mall.authz.constant.SecurityConstant;
import com.jiangyg.mall.authz.service.AdminAuthenticationService;
import com.jiangyg.mall.authz.service.CaptchaService;
import com.jiangyg.mall.authz.support.authentication.AccessDeniedExceptionHandler;
import com.jiangyg.mall.authz.support.authentication.AuthErrorExceptionHandler;
import com.jiangyg.mall.authz.support.authentication.AuthenticationRequestMatcher;
import com.jiangyg.mall.authz.support.authentication.admin.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.ExceptionTranslationFilter;

import java.util.ArrayList;
import java.util.List;

/**
 * 类描述：后台管理安全认证配置
 *
 * @author jiangyg
 * @date 2022-01-08
 */
@Order(1)
@Configuration(proxyBeanMethods = false)
public class SecurityAdminConfiguration extends WebSecurityConfigurerAdapter {

    /**
     * 验证码
     */
    private final CaptchaService captchaService;

    /**
     * 认证管理器
     */
    private AuthenticationManager authenticationManager;

    /**
     * 后台管理权限认证相关实现
     */
    private final AdminAuthenticationService adminAuthenticationService;

    @Autowired
    public SecurityAdminConfiguration(CaptchaService captchaService,
                                      AdminAuthenticationService adminAuthenticationService) {
        this.captchaService = captchaService;
        this.adminAuthenticationService = adminAuthenticationService;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 设置后台管理请求匹配策略
        http.requestMatcher(new AdminRequestMatcher());
        // 由于前后端分离且使用JWT，所以不需要启用 CSRF
        http.csrf().disable();
        // 基于 token 验证，不需要 session
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        // 禁用缓存
        http.headers().cacheControl();
        // 退出登录路径和退出登录处理器
        http.logout().logoutUrl(SecurityConstant.LOGOUT_URL).addLogoutHandler(logoutHandlerImpl());
        // 登录验证，注意此过滤器应为最前
        http.addFilter(loginAuthenticationFilter());
        // 设置访问权限校验
        http.authorizeRequests(authorize ->
                authorize.requestMatchers(new AuthenticationRequestMatcher())
                        .access("@adminAuthenticationService.hasAuthorities(authentication)"));
        // 访问权限前置过滤器（一定要在异常处理过滤器之后，否则异常不能正常处理）
        http.addFilterAfter(accessAuthenticationFilter(), ExceptionTranslationFilter.class);
        // 认证或者权限异常处理
        final ExceptionHandlingConfigurer<HttpSecurity> exceptionHandling = http.exceptionHandling();
        exceptionHandling.authenticationEntryPoint(new AuthErrorExceptionHandler());
        exceptionHandling.accessDeniedHandler(new AccessDeniedExceptionHandler());
    }

    /**
     * 功能描述：定义访问权限校验
     *
     * @return 访问权限校验
     */
    private AdminAccessAuthenticationFilter accessAuthenticationFilter() {
        return new AdminAccessAuthenticationFilter(authenticationManager());
    }

    /**
     * 功能描述：定义登录认证
     *
     * @return 登录认证
     */
    private AdminLoginAuthenticationFilter loginAuthenticationFilter() {
        return new AdminLoginAuthenticationFilter(captchaService, authenticationManager(), adminAuthenticationService);
    }

    /**
     * 功能描述：构建权限管理器
     *
     * @return 权限管理器
     */
    @Override
    protected AuthenticationManager authenticationManager() {
        // 如果已经初始化过则直接返回
        if (this.authenticationManager != null) {
            return this.authenticationManager;
        }
        // 定义权限验证实现列表
        List<AuthenticationProvider> providers = new ArrayList<>();
        // 追加用户名和密码授权器
        providers.add(daoAuthenticationProvider());
        // 追加访问权限处理
        providers.add(new AdminAccessAuthenticationProvider(adminAuthenticationService));
        // 返回并初始化认证管理器
        this.authenticationManager = new ProviderManager(providers);
        return this.authenticationManager;
    }

    /**
     * 功能描述：用户名和密码授权器
     *
     * @return 结果
     */
    private DaoAuthenticationProvider daoAuthenticationProvider() {
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
        daoAuthenticationProvider.setUserDetailsService(adminAuthenticationService);
        return daoAuthenticationProvider;
    }

    /**
     * 功能描述：后台管理加密器
     *
     * @return 结果
     */
    private BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 功能描述：定义退出登录处理器
     *
     * @return 退出登录处理器
     */
    private AdminLogoutHandler logoutHandlerImpl() {
        return new AdminLogoutHandler(adminAuthenticationService);
    }

}
